/**
 * 
 */
package jpb;

/**
 * Enumeration representing different memory units. Contains operations
 * to convert from one unit to another.<br>
 * <br>
 * 
 *  
 * @author Dariusz Kordonski
 */
public enum MemUnit {

	// Implementation based on the java.util.concurrent.TimeUnit
	BITS {
		public long toBits(long q) 		{ return q; }
		public long toBytes(long q) 	{ return q/(C1/C0); }
		public long toKiloBytes(long q) { return q/(C2/C0); }
		public long toMegaBytes(long q) { return q/(C3/C0); }
		public long toGigaBytes(long q) { return q/(C4/C0); }
		public long toTeraBytes(long q) { return q/(C5/C0); }
		public long convert(long q, MemUnit u) { return u.toBits(q); }
	},
	BYTES {
		public long toBits(long q) 		{ return scale(q, (C1/C0), MAX/(C1/C0)); }
		public long toBytes(long q) 	{ return q; }
		public long toKiloBytes(long q) { return q/(C2/C1); }
		public long toMegaBytes(long q) { return q/(C3/C1); }
		public long toGigaBytes(long q) { return q/(C4/C1); }
		public long toTeraBytes(long q) { return q/(C5/C1); }
		public long convert(long q, MemUnit u) { return u.toBytes(q); }
	},
	KILOBYTES {
		public long toBits(long q) 		{ return scale(q, (C2/C0), MAX/(C2/C0)); }
		public long toBytes(long q) 	{ return scale(q, (C2/C1), MAX/(C2/C1)); }
		public long toKiloBytes(long q) { return q; }
		public long toMegaBytes(long q) { return q/(C3/C2); }
		public long toGigaBytes(long q) { return q/(C4/C2); }
		public long toTeraBytes(long q) { return q/(C5/C2); }
		public long convert(long q, MemUnit u) { return u.toKiloBytes(q); }
	},
	MEGABYTES {
		public long toBits(long q) 		{ return scale(q, (C3/C0), MAX/(C3/C0)); }
		public long toBytes(long q) 	{ return scale(q, (C3/C1), MAX/(C3/C1)); }
		public long toKiloBytes(long q) { return scale(q, (C3/C2), MAX/(C3/C2)); }
		public long toMegaBytes(long q) { return q; }
		public long toGigaBytes(long q) { return q/(C4/C3); }
		public long toTeraBytes(long q) { return q/(C5/C3); }
		public long convert(long q, MemUnit u) { return u.toMegaBytes(q); }
	},
	GIGABYTES {
		public long toBits(long q) 		{ return scale(q, (C4/C0), MAX/(C4/C0)); }
		public long toBytes(long q) 	{ return scale(q, (C4/C1), MAX/(C4/C1)); }
		public long toKiloBytes(long q) { return scale(q, (C4/C2), MAX/(C4/C2)); }
		public long toMegaBytes(long q) { return scale(q, (C4/C3), MAX/(C4/C3)); }
		public long toGigaBytes(long q) { return q; }
		public long toTeraBytes(long q) { return q/(C5/C4); }
		public long convert(long q, MemUnit u) { return u.toGigaBytes(q); }
	},
	TERABYTES {
		public long toBits(long q) 		{ return scale(q, (C5/C0), MAX/(C5/C0)); }
		public long toBytes(long q) 	{ return scale(q, (C5/C1), MAX/(C5/C1)); }
		public long toKiloBytes(long q) { return scale(q, (C5/C2), MAX/(C5/C2)); }
		public long toMegaBytes(long q) { return scale(q, (C5/C3), MAX/(C5/C3)); }
		public long toGigaBytes(long q) { return scale(q, (C5/C4), MAX/(C5/C4)); }
		public long toTeraBytes(long q) { return q; }
		public long convert(long q, MemUnit u) { return u.toTeraBytes(q); }
	};
	
    // Credits to TimeUnit
    static final long C0 = 1L;			// bits
    static final long C1 = C0 * 8L;		// Bytes
    static final long C2 = C1 * 1024L;	// KBytes
    static final long C3 = C2 * 1024L;	// MBytes
    static final long C4 = C3 * 1024L;	// GBytes
    static final long C5 = C4 * 1024L;	// TBytes

    static final long MAX = Long.MAX_VALUE;

    /**
     * Scale d by m, checking for overflow.
     * @return scaled d*m
     */
    static long scale(long q, long m, long over) {
        if (q >  over) return Long.MAX_VALUE;
        if (q < -over) return Long.MIN_VALUE;
        return q * m;
    }
	
	// non-abstract methods - check out TimeUnit from OpenJDK for details
	
    
	/**
	 * 
	 * 
	 * @param q - quantity to convert
	 * @param unit - original memory unit of <code>q</code>
	 * @return
	 */
	public long convert(long q, MemUnit unit) {
		throw new AbstractMethodError();
	}
	
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toBits(long q) {
		throw new AbstractMethodError();
	}
	
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toBytes(long q) {
		throw new AbstractMethodError();
	}
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toKiloBytes(long q) {
		throw new AbstractMethodError();
	}
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toMegaBytes(long q) {
		throw new AbstractMethodError();
	}
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toGigaBytes(long q) {
		throw new AbstractMethodError();
	}
	/**
	 * 
	 * @param q - quantity to convert
	 * @return
	 */
	public long toTeraBytes(long q) {
		throw new AbstractMethodError();
	}

}
